package stud.mpgame.snapshot;

import flash.utils.Dictionary;
import flash.utils.SetIntervalTimer;
import flash.utils.TypedDictionary;

import stud.mpgame.network.Message;
import stud.mpgame.network.UserObject;

import flash.utils.IDataInput;

/**
 * ...
 * @author sydy[sydyM6@gmail.com]
 */
/*abstract*/ class ASnapshot implements ISnapshot, extends UserObject {

	private var extrapolatedGameObjectIDs:TypedDictionary<Int, Int>;
	private var lastCommandApplyTime:Int;
	private var time:Int;
	
	private function new() {
		super();
		if (Type.getClass(this) == ASnapshot) {
			throw "Abstract class, no instantiation";
		}
		extrapolatedGameObjectIDs = new TypedDictionary<Int, Int>();
	}
	
	public function getTime():Int {
		return time;
	}
	
	public function setTime(time:Int):Void {
		time = this.time;
	}
	
	public function interpolate(deltaTime:Int, serverSnapshot:ISnapshot):Void {
		var serverASS:ASnapshot = cast(serverSnapshot, ASnapshot);
		
		removeTempGameObjectIDs(serverASS.lastCommandApplyTime);
		
		var gameObjects:List<IGameObject> = getAllGameObjects();
		for (gameObject in gameObjects) {
			if (extrapolatedGameObjectIDs.exists(gameObject.getID())) {
				gameObject.extrapolate(deltaTime);
			} else {
				gameObject.interpolate(deltaTime, serverASS.getGameObject(gameObject.getID()), serverASS.time - time);
			}
		}
		time += deltaTime;
	}
	
	/*abstract*/ public function getGameObject(id:Int):IGameObject {
		throw "Abstract method";
		return null;
	}
	
	public function extrapolate(deltaTime:Int):Void {
		var gameObjects:List<IGameObject> = getAllGameObjects();
		for (gameObject in gameObjects) {
			gameObject.extrapolate(deltaTime);
		}
		time += deltaTime;
	}
	
	public function synchronize(serverSnapshot:ISnapshot):Void {
	}
	
	/*abstract*/ public function getAllGameObjects():List<IGameObject> {
		throw "Abstract method";
		return null;
	}
	
	public function getLastCommandApplyTime():Int {
		return lastCommandApplyTime;
	}
	
	public function addTempGameObjects(gameObject:IGameObject, commandTime:Int):Void {
		extrapolatedGameObjectIDs.set(gameObject.getID(), commandTime);
	} 
	
	public function removeTempGameObjectIDs(lastCommandApplyTime:Int):Void {
		var keys:Array<Int> = extrapolatedGameObjectIDs.keys();
		for (id in keys) {
			if (extrapolatedGameObjectIDs.get(id) <= lastCommandApplyTime) {
				extrapolatedGameObjectIDs.delete(id);
			}
		}
	}
	
	public override function readExternal(input:IDataInput, message:Message):Void {
		lastCommandApplyTime = input.readInt();
		time = input.readInt();
	}

	public function toString() {
		return "GW: " + time + "; " + getLastCommandApplyTime();
	}	
}
